package com.king.hessian;


import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;

import com.caucho.hessian.io.SerializerFactory;
import com.caucho.hessian.server.HessianSkeleton;

import lombok.extern.slf4j.Slf4j;


/**
 * 一个简单的url mapping。在spring启动时把所有带有@Rpc注解的bean加入映射表（以RpcServer实例的形式），
 * 在接到请求时从映射表查询对应的bean，请求给对应的RpcServer。
 *
 * 本类支持.rpc和.hessian两种url定义是因为历史原因，曾经支持过.json是故。
 */
@Slf4j
public class RpcFilter implements Filter, BeanPostProcessor {

    @Autowired(required = false)
    private SerializerFactory serializerFactory;

    private final Map<String, RpcServer> servers = new HashMap<String, RpcServer>();

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetClass = bean.getClass();
        Rpc rpc = targetClass.getAnnotation(Rpc.class);
        if (rpc != null) {
            ProxyFactory weaver = new ProxyFactory(targetClass.getInterfaces());
            NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
            advisor.addMethodName("*");
            advisor.setAdvice(new RpcContextMethodInterceptor(beanName, bean));
            weaver.addAdvisor(advisor);
            weaver.setTarget(bean);
            Object proxyObject = weaver.getProxy();

            final String[] urls = rpc.url();
            for (String url : urls) {
                Class<?> serviceInterface = rpc.serviceInterface();
                if (url.endsWith(".rpc") || url.endsWith(".hessian")) {
                    String actualUrl = StringUtils.replace(url, ".rpc", ".hessian");
                    HessianSkeleton hessianSkeleton = new HessianSkeleton(proxyObject, serviceInterface);
                    if (log.isInfoEnabled()) {
                        log.info(String.format("安装hessian rpc %s = %s", actualUrl, serviceInterface));
                    }
                    servers.put(actualUrl, new HessianRpcServerImpl(hessianSkeleton, serializerFactory));
                }
            }
        }
        return bean;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {
        if (request instanceof HttpServletRequest) {
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse resp = (HttpServletResponse) response;
            String uri = req.getRequestURI();
            RpcServer server = servers.get(uri);
            if (log.isDebugEnabled()) {
                log.debug(String.format("开始处理rpc %s", uri));
            }
            if (server != null) {
                server.handle(req, resp);
                response.getOutputStream().flush();
                return;
            } else if (log.isDebugEnabled()) {
                log.debug(String.format("结束处理rpc %s", uri));
            }
        }
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    protected void setSerializerFactory(SerializerFactory serializerFactory) {
        this.serializerFactory = serializerFactory;
    }
}
